在项目中使用eclipse编写了一个Android的Library工程,现需要将工程导出成jar包,并且混淆,然后提交给外部使用。要求除了外部需要调用的接口,其他部分需要proguard混淆和优化。
下面是实现的过程。
导出jar包
获取未混淆的jar包,这里有两种方式:
- 使用eclipse的export导出功能
- 直接从bin文件夹获取
1方法就是通用的方法,先在library工程上右键,选择export,然后选择java标签下的jar file,然后将除了src之外的所有文件夹取消勾选,导出即可,如下图:
而2方法简单的多。library工程在被其他工程依赖时,会自动在bin目录下生成编译好的jar包,直接拿此jar包用即可。经过反编译查看此jar包与export导出的jar包的内容是一样的,可放心使用。
使用proguard混淆
平时在eclipse打混淆包时,eclipse会自动找到你在project.properties中定义的proguard配置,并通过命令行调用proguard进行混淆、优化。而这里,我们可以使用proguard提供的gui工具进行混淆。
gui工具在android的sdk目录下的tools/proguard/lib/proguardgui.jar,双击打开即可。界面如下:
1.先点击底部的load configuration,加载写好的配置文件(请先学习proguard的配置语法):
2.然后右边的选项点到Input/Output选项,如下图:
3.在上面add input添加要混淆的jar包,add output添加要生成的jar包。(这里很不合理,因为我要生成的jar包肯定是现在还不存在的,但是它在选择框里要求你选择一个已经存在的jar包。没办法,这里我先用”touch des.jar”新建了一个空白的文件,然后才选的)。
4.然后在下面添加依赖jar包。因为我的工程只用到了android.jar,所以就只把它添加进去了。
5.然后左侧的下面几个看不懂的tab就不要点了,也不要点下面的next跟着它的节奏走,因为proguard的配置我们之前已经加载进去了。所以现在直接点Process标签,然后点击最下方的“Process!”按钮。过一会儿就会得到优化好的、混淆的jar包。
如果过程中出现问题,那么一定是proguard的配置文件写的不对!修改此文件,然后重复上述流程。
Proguard配置文件
其实核心还是这个文件,首先,我在网上看到一个通用的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| -optimizationpasses 5 -dontusemixedcaseclassnames -dontskipnonpubliclibraryclasses -dontpreverify -verbose -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* -keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider -keep public class * extends android.app.backup.BackupAgentHelper -keep public class * extends android.preference.Preference -keep public class com.android.vending.licensing.ILicensingService -keepclasseswithmembernames class * { native <methods>; } -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet); } -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet, int); } -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; }
|
直接使用此配置作为混淆文件是不行的,我100k左右的jar包经过上述配置混淆后只剩8k,反编译后发现只剩下了寥寥几个类。原来是我的对外接口没有暴露出来,结果被当作无用的代码优化掉了。
因此对外接口需要暴露出来不混淆,在配置文件底部增加:
1 2 3 4
| -keep public class com.test.MyAgent { public <fields>; public <methods>; }
|
其中MyAgent就是我的对外接口。再次混淆就会发现混淆后的jar包果然变大了。
然而,此时发现其实还有一些类是需要对外暴露的。于是继续添加配置:
1 2 3 4 5 6 7 8 9
| -keep public class com.test.MyInterface { public <fields>; public <methods>; } -keep public class * implements com.test.MyInterface { public <fields>; public <methods>; }
|
这里规定不混淆MyInterface以及实现了MyInterface接口的文件。
然后还有问题,我将所有的枚举都作为内部类放在了一个EnumUtil中,这些枚举外部也有调用的需要。因此添加:
1 2 3 4 5 6 7 8 9
| -keep public class com.test.utils.EnumUtil { *; } -keepnames class com.test.utils.EnumUtil$* { *; } -keepattributes InnerClasses
|
规定不混淆EnumUtil中的内部类即可。
2016.05.22 update:
jar包里如果用到资源文件千万别用反射的方法获取,因为混淆之后会出问题。。。先是我,然后是两个同事都掉进了这篇文章的坑里了。
直接用context.getResources().getIdentify()的方法获取资源id即可。
EOF